package cache

import (
	"sync"
	"time"
	"container/list"
	"errors"
)

//缓存对象
type MemoryCache struct {
	MaxEntries int //最大缓存项
	bucket *CacheContainer //缓存容器
	objectPool *sync.Pool
	rwMutex *sync.RWMutex //过期列表读写锁
}
//一个缓存容器
type CacheContainer struct {
	bucket map[string]*list.Element //缓存容器
	lruList *list.List //节点链表结果
}

//一个单独的缓存项
type CacheEntry struct {
	Key string
	Value interface{}
	Expiration int64
}


//获取一个内存缓存对象并设置默认参数
//默认每个储存容器初始化大小为5W
//@maxEntries 缓存项储存最大值，超过该值会被移除缓存
//@capacity 初始缓存容量
func NewMemoryCache(maxEntries int,capacity int) *MemoryCache {
	return &MemoryCache{
		MaxEntries:maxEntries,
		bucket: &CacheContainer{ bucket: make(map[string]*list.Element,capacity),lruList: list.New()},
		rwMutex: new(sync.RWMutex),
		objectPool: &sync.Pool{
			New:func() interface {}{
				return &CacheEntry{}
			},
		},
	};
}

//判断指定的元素是否过期，如果过期则添加到过期列表中
func (this *MemoryCache) pushExpiredCacheEntry(el *list.Element,forcibly bool) bool {
	item,ok := el.Value.(*CacheEntry);
	//如果缓存已过期则转移到过期列表中暂存
	if(time.Now().Unix() > item.Expiration || forcibly){
		this.bucket.lruList.Remove(el);
		if(ok){
			delete(this.bucket.bucket,item.Key);
			this.objectPool.Put(&item);
		}
		return true;
	}
	this.bucket.lruList.MoveToBack(el);
	return false;
}

//获取指定区域内的缓存数量
func (this *MemoryCache) GetCount() int {
	if(this.bucket == nil){
		return 0;
	}

	return this.bucket.lruList.Len();
}


//获取一个值表示指定的键名是否在缓存中
//如果存在则返回true否则返回false
func (this *MemoryCache) Contains(key string) bool {
	if(this.bucket == nil){
		return false;
	}
	this.rwMutex.RLock();
	if el,ok := this.bucket.bucket[key];ok{
		this.rwMutex.RUnlock();
		this.rwMutex.Lock();
		defer this.rwMutex.Unlock();
		this.pushExpiredCacheEntry(el,false);
		return !ok;
	}
	this.rwMutex.RUnlock();
	return false;

}

//获取指定键名的缓存
//第一个参数为缓存对象只，第二个参数为是否获取成功。如果成功则返回true，否则返回false
//该方法使用了读锁，在获取容器是如果容器存在则使用读锁，否则使用读写锁创建容器
//当获取值时，会扫描当前值是否过期，如果过期则直接删除并返回false，否则会重置当前值得最后访问时间
func (this *MemoryCache) Get(key string) (interface{} ,bool){
	if(this.bucket == nil){
		return nil,false;
	}
	this.rwMutex.Lock();
	defer this.rwMutex.Unlock();

	if el,ok := this.bucket.bucket[key];ok{
		if ok = this.pushExpiredCacheEntry(el,false);ok{
			return nil,false;
		}
		return el.Value.(*CacheEntry).Value,true;

	}
	return nil,false;
}


//获取缓存中一个缓存实体
func (this *MemoryCache) GetCacheEntry(key string) (*CacheEntry,bool){
	if(this.bucket == nil){
		return &CacheEntry{},false;
	}
	this.rwMutex.Lock();
	defer this.rwMutex.Unlock();

	if el,ok := this.bucket.bucket[key];ok{
		if ok = this.pushExpiredCacheEntry(el,false);ok{
			return &CacheEntry{},false;
		}
		return el.Value.(*CacheEntry),true;

	}
	return &CacheEntry{},false;
}

/***
* 	添加一个值到缓存中
*	@key 缓存键名
*	@value 缓存值
*	@duration 缓存时间
 */
func (this *MemoryCache) Add(key string,value interface{},duration time.Duration) error {
	if(this.bucket == nil){
		return errors.New("缓存容器没有初始化");
	}
	if(len(key) >= 1024){
		return errors.New("缓存键名长度必须小于1024字节")
	}

	this.rwMutex.Lock();
	defer this.rwMutex.Unlock();

	if el,ok := this.bucket.bucket[key];ok {

		if item,ok := el.Value.(*CacheEntry);ok{
			item.Value = value;
			item.Expiration = time.Now().Add(duration).Unix();
			return nil;
		}
	}
	var cacheItem *CacheEntry;
	var ok bool;

	cacheElement := this.objectPool.Get();


	if cacheItem,ok = cacheElement.(*CacheEntry); ok == false{
		cacheItem = &CacheEntry{};
	}

	cacheItem.Key = key;
	cacheItem.Value = value;
	cacheItem.Expiration = time.Now().Add(duration).Unix();


	el := this.bucket.lruList.PushFront(cacheItem);
	this.bucket.bucket[key] = el;
	if(this.bucket.lruList.Len() > this.MaxEntries){
		temp := this.bucket.lruList.Back();
		this.pushExpiredCacheEntry(temp,true);
	}

	return nil;
}
/**
	从缓存中移除指定的缓存项
 */
func (this *MemoryCache) Remove(key string) (interface{},bool) {
	if(this.bucket ==nil){
		return nil,false;
	}
	this.rwMutex.RLock();
	if el,ok := this.bucket.bucket[key];ok{
		this.rwMutex.RUnlock();
		this.rwMutex.Lock();
		defer this.rwMutex.Unlock();

		delete(this.bucket.bucket,key);
		this.bucket.lruList.Remove(el);
		if item,ok := el.Value.(*CacheEntry);ok{
			this.objectPool.Put(&item);
		}

		return el.Value.(*CacheEntry).Value,true;
	}
	return nil,true;
}
/**
	清空指定区域中的所有缓存
 */
func (this *MemoryCache) Clear() {
	this.rwMutex.Lock();
	defer this.rwMutex.Unlock();

	this.bucket.lruList = list.New();
	this.bucket.bucket = make(map[string]*list.Element,this.MaxEntries);
}
